/*
 * Copyright (c) 2010, Jim Hollinger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of Jim Hollinger nor the names of its contributors
 *     may be used to endorse or promote products derived from this
 *     software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <string>

#include "debughelper/debughelper.h"
#include "logger/logger.h"
#include "pcap/pcapfile.h"
#include "datachannel/pcapchannel.h"


using namespace datachannel;


/**
 ****************************************************************************
 * @brief  Constructor
 ****************************************************************************
**/
PcapChannel::PcapChannel(logger::Logger *logger)
    : DataChannel(logger), pcap(NULL) {
    pcap = new pcap::PcapFile();
    mode = READ;
    type = BINARY;
}

/**
 ****************************************************************************
 * @brief  Destructor
 ****************************************************************************
**/
PcapChannel::~PcapChannel() {
    delete pcap;
    pcap = NULL;
}

/**
 ****************************************************************************
 * @brief  Opens the file.
 *
 * @param  descriptor  Filename to open
 *                      filename[:read|:write][:binary|:text]
 *                      default = read + binary
 ****************************************************************************
**/
bool PcapChannel::open(const std::string &descriptor) {
    bool flag = false;

    if (pcap != NULL) {
        pcap->close();

        size_t i = descriptor.find_first_of(':');
        if (i == std::string::npos) {
            filename = descriptor;
        } else {
            filename = descriptor.substr(0, i);
            if (descriptor.find(":read") != std::string::npos) {
                setFileAccessMode(READ);
            }
            if (descriptor.find(":write") != std::string::npos) {
                setFileAccessMode(WRITE);
            }
            if (descriptor.find(":binary") != std::string::npos) {
                setFileContentType(BINARY);
            }
            if (descriptor.find(":text") != std::string::npos) {
                setFileContentType(TEXT);
            }
        }
        if (mode == READ) {
            if (type == BINARY) {
                flag = pcap->open(filename.c_str(), "rb");
            } else {  // TEXT
                flag = pcap->open(filename.c_str(), "rt");
            }
            if (flag) {
                log->info("PcapChannel opened \"%s\" for reading",
                    filename.c_str());
            } else {
                log->error("PcapChannel: Unable to open \"%s\" for reading",
                    filename.c_str());
            }
        } else {  // WRITE
            if (type == BINARY) {
                flag = pcap->open(filename.c_str(), "wb");
            } else {  // TEXT
                flag = pcap->open(filename.c_str(), "wt");
            }
            if (flag) {
                log->info("PcapChannel opened \"%s\" for writing",
                    filename.c_str());
            } else {
                log->error("PcapChannel: Unable to open \"%s\" for writing",
                    filename.c_str());
            }
        }
    }

    return flag;
}

/**
 ****************************************************************************
 * @brief  Closes the file.
 ****************************************************************************
**/
bool PcapChannel::close() {
    bool flag = false;

    if (pcap != NULL) {
        pcap->close();
        flag = true;
    }

    return flag;
}

/**
 ****************************************************************************
 * @brief  Reads a line from the file.
 ****************************************************************************
**/
int PcapChannel::read(void *buffer, int maxCount) {
    int numRead = 0;

    if (pcap == NULL) {
        log->warn("PcapChannel.read: \"%s\" not opened", filename.c_str());
    } else {
        if (pcap->isOpen()) {
            if (mode == READ) {
                if (pcap->readPacket()) {
                    numRead = pcap->getPacketLength();
                    if (numRead > maxCount) {
                        numRead = maxCount;
                    }
                    memcpy(buffer, pcap->getPacketData(), numRead);
                }
            } else {
                log->warn("PcapChannel.read: \"%s\" not opened for reading",
                    filename.c_str());
            }
        } else {
            log->warn("PcapChannel.read: \"%s\" not opened", filename.c_str());
        }
    }

    return numRead;
}

/**
 ****************************************************************************
 * @brief  Writes to the file.
 ****************************************************************************
**/
int PcapChannel::write(const void *buffer, int count) {
    int numWritten = 0;

    if (pcap == NULL) {
        log->warn("PcapChannel.write: \"%s\" not opened", filename.c_str());
    } else {
        if (pcap->isOpen()) {
            if (mode == WRITE) {
                log->warn("PcapChannel.write: not implemented");
            } else {
                log->warn("PcapChannel.write: \"%s\" not opened for writing",
                    filename.c_str());
            }
        } else {
            log->warn("PcapChannel.write: \"%s\" not opened", filename.c_str());
        }
    }

    return numWritten;
}

/**
 ****************************************************************************
 * @brief  Sets the file access mode.
 *
 * @param  mode  File access mode (read or write)
 ****************************************************************************
**/
void PcapChannel::setFileAccessMode(FileAccessMode new_mode) {
    mode = new_mode;
}

/**
 ****************************************************************************
 * @brief  Sets the file content type.
 *
 * @param  type  File content type (binary or text)
 ****************************************************************************
**/
void PcapChannel::setFileContentType(FileContentType new_type) {
    type = new_type;
}
